﻿package api;

typedef UserInfo = {
	var uid: Int;
	var firstName: String;
	var lastName: String;
	var sex: Int; // 2 => male, 1 => female, 0 => unknown
	var birthDate: String; // maybe Date 
	var city: Int;
	var country: Int;
	var timezone: Int;
	var photo: String;
	var photoMedium: String;
	var photoBig: String;
};

typedef Ad = {
	var title: String;
	var description: String;
 	var photo: String;
	var link : String;
};

typedef ScoreInfo = {
	var uid: Int;
	var name: String;
 	var score: Int;	
};

typedef MessageInfo = {
	var uid: Int;
	var message: String;
	var time: Int;
	var name: String;
}
enum NameCase {
	nom;
	gen;
	dat;
	acc;
	ins;
	abl;
}

import haxe.xml.Fast;

class DAO {
	private static inline var IS_APP_USER: String = "isAppUser";
	private static inline var GET_PROFILES: String = "getProfiles";
	private static inline var GET_FRIENDS: String = "getFriends";
	private static inline var GET_APP_FRIENDS: String = "getAppFriends";
	private static inline var GET_USER_BALANCE: String = "getUserBalance";
	private static inline var GET_CITIES: String = "getCities";
	private static inline var GET_COUNTRIES: String = "getCountries";
	private static inline var GET_VARIABLE: String = "getVariable";
	private static inline var GET_VARIABLES: String = "getVariables";
	private static inline var PUT_VARIABLE: String = "putVariable";
	private static inline var GET_HIGH_SCORES: String = "getHighScores";
	private static inline var SET_USER_SCORE: String = "setUserScore";
	private static inline var GET_MESSAGES: String = "getMessages";
	private static inline var SEND_MESSAGE: String = "sendMessage";
	private static inline var GET_SERVER_TIME: String = "getServerTime";
	private static inline var GET_USER_INFO: String = "getUserInfo";
	private static inline var GET_USER_INFO_EX: String = "getUserInfoEx";
	private static inline var GET_USER_SETTINGS : String = "getUserSettings";
	private static inline var GET_ADS : String = "getAds";
	
	private static inline var BAD_RESPONSE: String = "Не удалось установить соединение с сервером ВКонтакте. Попробуйте перезапустить приложение.";
	
	var requestManager : RequestManager;

	public function new(apiId: Int, apiSecret: String, viewerId: Int, testMode: Bool = false) {
		requestManager = new RequestManager(apiId, apiSecret, viewerId, testMode);
	}

	public static function getError(fast : Fast): String {
		if (fast.node.error_code.innerData == "1") {
			return "На сервере ВКонтакте произошла неизвестная ошибка. Попробуйте перезапустить приложение или зайти позже.";
		}
		if (fast.node.error_code.innerData == "2") {
			return "Приложение отключено разработчиками. Попробуйте зайти позже.";
		}
		if (fast.node.error_code.innerData == "4") {
			return "Не удалось установить соединение с сервером ВКонтакте. Попробуйте зайти позже.";
		}
		if (fast.node.error_code.innerData == "5") {
			return "Не удалось выполнить авторизацию пользователя. Попробуйте зайти позже.";
		}
		if (fast.node.error_code.innerData == "6") {
			return "Не удалось установить соединение с сервером ВКонтакте. Попробуйте перезагрузить страницу.";
		}
		if (fast.node.error_code.innerData == "7") {
			return "Для корректной работы приложения необходимо разрешить доступ ко всем действиям в настройках приложения.";
		}
		return BAD_RESPONSE;
	}
	
	// Users
	
	public function isAppUser(onComplete: Bool -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				onComplete(fast.innerData == "1");
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method: IS_APP_USER, params: {}, onComplete: newOnComplete, onError: onError});
	}
	
	public function getProfiles(uids: Array < Int >, ?nameCase: NameCase, ?fields: Array<String>, onComplete: Array < UserInfo > -> Void, onError: String -> Void) { 
		if (fields == null) { 
			fields = ["uid", "first_name", "second_name", "sex", "bdate", "city", "country", "timezone", "photo", 
			"photo_medium", "photo_big"];
		}
		if (nameCase == null) {
			nameCase = NameCase.nom;
		}
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var infos = [];
				for (user in fast.nodes.user) {
					// empty user info
					var info = {uid: null, firstName: null, lastName: null, sex: 0, birthDate: null, 
						city: null, country: null, timezone: null, photo: null, photoMedium: null, photoBig: null};
						
						if (user.hasNode.uid) {
							info.uid = Std.parseInt(user.node.uid.innerData);
						}
						if (user.hasNode.first_name) {
							info.firstName = user.node.first_name.innerData;
						} 
						if (user.hasNode.last_name) {
							info.lastName = user.node.last_name.innerData;
						}
						if (user.hasNode.sex) {
							info.sex = Std.parseInt(user.node.sex.innerData);
						}
						if (user.hasNode.bdate) {
							info.birthDate = user.node.bdate.innerData;
						}
						if (user.hasNode.city) {
							info.city = Std.parseInt(user.node.city.innerData);
						}
						if (user.hasNode.country) {
							info.country = Std.parseInt(user.node.country.innerData);
						}
						if (user.hasNode.timezone) {
							info.timezone = Std.parseInt(user.node.timezone.innerData);
						}
						if (user.hasNode.photo) {
							info.photo = user.node.photo.innerData;
						}
						if (user.hasNode.photo_medium) {
							info.photoMedium = user.node.photo_medium.innerData;
						}
						if (user.hasNode.photo_big) {
							info.photoBig = user.node.photo_big.innerData;
						}							

					infos.push(info);
				}
				onComplete(infos);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}			
		}
		requestManager.add({method: GET_PROFILES, params: {uids: uids.join(","), fields: fields.join(",")}, onComplete: newOnComplete, onError: onError});						
	}
			
	public function getFriends(onComplete: Array<Int> -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var uids = [];
				for (uid in fast.nodes.uid) {
					uids.push(Std.parseInt(uid.innerData));
				}
				onComplete(uids);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		
		requestManager.add({method: GET_FRIENDS, params: {}, onComplete: newOnComplete, onError: onError});
	}
		
	public function getAppFriends(onComplete: Array<Int> -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var uids = [];
				for (uid in fast.nodes.uid) {
					uids.push(Std.parseInt(uid.innerData));
				}
				onComplete(uids);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		
		requestManager.add({method: GET_APP_FRIENDS, params: {}, onComplete: newOnComplete, onError: onError});
	}
	
	public function getUserBalance(onComplete: Int -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var value = (fast.node.balance.hasNode.innerData) ? fast.node.balance.innerData : "";
				onComplete(Std.parseInt(value));
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method: GET_USER_BALANCE, params: { }, onComplete: newOnComplete, onError: onError});						
	}

	// Geography objects
		
	public function getCities(cids: Array<Int>, onComplete : IntHash<String> -> Void, onError: String -> Void) {
		var cities = cids.join(",");
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var hash = new IntHash<String>();
				for (city in fast.nodes.city) {
					var cityId = Std.parseInt(city.node.cid.innerData);
					var cityName = city.node.name.innerData;
					hash.set(cityId, cityName);
				}
				onComplete(hash);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		
		requestManager.add({method: GET_CITIES, params: {cids: cities}, onComplete: newOnComplete, onError: onError});						
	}

	public function getCountries(cids: Array < Int > , onComplete : IntHash < String > -> Void, onError: String -> Void) {
		var countries = cids.join(",");
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var hash = new IntHash<String>();
				try {
					for (country in fast.nodes.country) {
						var countryId = Std.parseInt(country.node.cid.innerData);
						var countryName = country.node.name.innerData;
						hash.set(countryId, countryName);
					}
					onComplete(hash);
				} catch (unknown : Dynamic) {
					onError(BAD_RESPONSE);
				}
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method: GET_COUNTRIES, params: {cids: countries}, onComplete: newOnComplete, onError: onError});						
	}

	// other methods

	public function getVariable(key: Int, userId: Null < Int > , sessionId: Int, onComplete: String -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {	
				try {
					onComplete(fast.innerData);
				} catch (unknown : Dynamic) {
					onComplete("");
				}
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		if (userId <= 0) {
			userId = null;
		} 
		if (sessionId < 0) {
			sessionId = 0;
		}
		requestManager.add({method: GET_VARIABLE, params: {key : key, user_id : userId, session : sessionId}, onComplete: newOnComplete, onError: onError});
	}

	public function getVariables(key: Int, count: Int, userId: Null<Int>, sessionId : Int, onComplete: IntHash<String> -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var hash = new IntHash<String>();
				for (variable in fast.nodes.var_info) {
					var keyValue;
					try {
						keyValue = variable.node.key.innerData;
					} catch (unknown : Dynamic) {
						keyValue = "-1";
					}
					var key = Std.parseInt(keyValue);
					var value;
					try {
						value = variable.node.value.innerData;
					} catch (unknown : Dynamic) {
						value = "-1";
					}
					hash.set(key, value);
				}
				onComplete(hash);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method: GET_VARIABLES, params: {key : key, count : count, user_id : userId, session : sessionId}, onComplete: newOnComplete, onError: onError});
	}

	public function putVariable(key : Int, value : String, userId : Null < Int > , sessionId : Int, onComplete: Bool -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				onComplete(true);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		if (userId <= 0) {
			userId = null;
		} 
		if (sessionId < 0) {
			sessionId = 0;
		}
		requestManager.add({method: PUT_VARIABLE, params: {key : key, value : value, user_id : userId, session : sessionId}, onComplete: newOnComplete, onError: onError});
	}

	public function getHighScores(onComplete: Array<ScoreInfo> -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
		    var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var scores = new Array<ScoreInfo>();
				for (scoreInfo in fast.nodes.score_info) {
					var uid = Std.parseInt(scoreInfo.node.user_id.innerData);
					var name = scoreInfo.node.user_name.innerData;
					var score = Std.parseInt(scoreInfo.node.score.innerData);
					scores.push({uid: uid, name: name, score: score});
				}
				onComplete(scores);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		
		requestManager.add({method: GET_HIGH_SCORES, params: {}, onComplete: newOnComplete, onError: onError});						
	} 

	public function setUserScore(score : Int, onComplete: Void -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				onComplete();
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method: SET_USER_SCORE, params: {score: score}, onComplete: newOnComplete, onError: onError});
	}

	public function getMessages(?count: Null<Int>, onComplete: Array<MessageInfo> -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var messages = new Array<MessageInfo>();
				for (msgInfo in fast.nodes.message_info) {
					var uid = Std.parseInt(msgInfo.node.user_id.innerData);
					var name = msgInfo.node.user_name.innerData;
					var time = Std.parseInt(msgInfo.node.time.innerData);
					var message = msgInfo.node.message.innerData;
					messages.push({uid: uid, name: name, time: time, message: message});
				}
				onComplete(messages);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method: GET_MESSAGES, params: {messages_to_get: count}, onComplete: newOnComplete, onError: onError});						
	}

	public function sendMessage(message: String, onComplete: Void -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
		    var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				onComplete();
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method: SEND_MESSAGE, params: {message : message}, onComplete: newOnComplete, onError: onError});						
	}

	public function getServerTime(onComplete: Date -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
		    var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var value = (fast.hasNode.innerData) ? fast.innerData : "";
			    var time = Std.parseFloat(value);
				onComplete(Date.fromTime(time * 1000));
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
	
		requestManager.add({method: GET_SERVER_TIME, params: {}, onComplete: newOnComplete, onError: onError});						
	}
	
	public function getUserInfo(onComplete: {uid: Int, name: String} -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
		    var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var uidValue = (fast.node.user_id.hasNode.innerData) ? fast.node.user_id.innerData : "";
			    var uid = Std.parseInt(uidValue);
				var name = (fast.node.user_name.hasNode.innerData) ? fast.node.user_name.innerData : "";
				onComplete({uid: uid, name: name});
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
	
		requestManager.add({method: GET_USER_INFO, params: {}, onComplete: newOnComplete, onError: onError});						
	}
	
	public function getUserSettings(onComplete: Int -> Void, onError : String -> Void) {
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {	
				try {
					onComplete(Std.parseInt(fast.node.settings.innerData));
				} catch (unknown : Dynamic) {
					onComplete(0);
				}
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
		requestManager.add({method : GET_USER_SETTINGS, params : {}, onComplete : newOnComplete, onError:  onError});
	}
	
	public function getAds(onComplete: Array < Ad > -> Void, onError: String -> Void) { 
		var newOnComplete = function (xml: Xml) {
			var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
				var ads = [];
				for (a in fast.nodes.ad) {
					// empty user info
					var ad = { title: null, description: null, photo: null, link : null };
						if (a.hasNode.title) {
							ad.title = a.node.title.innerData;
						}
						if (a.hasNode.description) {
							ad.description = a.node.description.innerData;
						} 
						if (a.hasNode.photo) {
							ad.photo = a.node.photo.innerData;
						}
						if (a.hasNode.link) {
							ad.link = a.node.link.innerData;
						}
					ads.push(ad);
				}
				onComplete(ads);
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}			
		}
		requestManager.add({method: GET_ADS, params: { }, onComplete: newOnComplete, onError: onError});						
	}
	
	/*public function getUserInfoEx(onComplete: {uid: Int, name: String, city: String, birthDate: String, photo: String} -> Void, onError: String -> Void) {
		var newOnComplete = function (xml: Xml) {
		    var fast = new Fast(xml.firstElement());
			if (fast.name == "response") {
			    var uid = Std.parseInt(head.elementsNamed("user_id").next().firstChild().nodeValue);
				var name = head.elementsNamed("user_name").next().firstChild().nodeValue;
				var city = head.elementsNamed("user_city").next().firstChild().nodeValue;
				var birthDate = head.elementsNamed("user_bdate").next().firstChild().nodeValue;
				var photo = head.elementsNamed("user_photo").next().firstChild().nodeValue;
				onComplete({uid: uid, name: name, city: city, birthDate: birthDate, photo: photo});
			} else if (fast.name == "error") {
				onError(getError(fast));
			} else {
				onError(BAD_RESPONSE);
			}
		}
	
		requestManager.add(GET_USER_INFO_EX, {}, newOnComplete, onError);						
	}*/
	
}	
